<script>

function DP(in_obj)
{
	return;
	console.log(in_obj);
}

var gRatio = {
	cache : [null],
	/*
		0 : null
		1 : 
			len    : [1]
			sumInc : [1]
			sumDec : [1]
		2 : 
			len    : [l0, l1] ... (l0 + l1 = 1)
			sumInc : [I0, I1] ... (I1 = 1)
			sumDec : [D0, D1] ... (D1 = 1)
		3 : 
			len    : [l0, l1, l2] ... (l0 + l1 + l2 = 1)
			sumInc : [I0, I1, I2] ... (I2 = 1)
			sumDec : [D0, D1, D2] ... (D2 = 1)
		...
	*/
	configure : function(in_inc, in_cnt) {
		var firstValue = 1;
		var weights = [];
		for (var i = 0; i < in_cnt; i++) {
			weights.push(firstValue + in_inc * i);
		}
		this.composeCache(weights);
	},
	composeCache : function(in_weights) {
		for (var step = 1; step <= in_weights.length; step++) {
			this.cache[step] = {
				len : [],
				sumInc : [],
				sumDec : []
			}
			var denom = 0;
			for (var i = 0; i < step; i++) {
				this.cache[step].len[i] = in_weights[i];
				denom = denom + this.cache[step].len[i];
			}
			for (var i = 0; i < step; i++) {
				this.cache[step].len[i] /= denom;
			}
			for (var i = 0; i < step; i++) {
				this.cache[step].sumInc[i] = 0;
				this.cache[step].sumDec[i] = 0;
				for (var j = 0; j <= i; j++) {
					this.cache[step].sumInc[i] += this.cache[step].len[j];
					this.cache[step].sumDec[i] += this.cache[step].len[(step - 1) - j];
				}
			}
		}
	},
	get : function(in_step, in_prop, in_num) {
		return this.cache[in_step][in_prop][in_num];
	},
	getLength : function(in_step, in_num) {
		return this.get(in_step, 'len', in_num);
	},
	getSumInc : function(in_step, in_num) {
		return this.get(in_step, 'sumInc', in_num);
	},
	getSumDec : function(in_step, in_num) {
		return this.get(in_step, 'sumDec', in_num);
	},
	getSteps : function() {
		return this.cache.length;
	},
	test : function() {
		var canvasHeight = 100;
		var dx = 10;
		var testData = {
			inc : 1,
			cnt : 20
		};
		this.configure(testData.inc, testData.cnt);
		/* canvas */
		var canvas = document.createElement('CANVAS');
		canvas.width = (testData.cnt - 1) * dx;
		canvas.height = canvasHeight;
		document.write('<div></div>');
		document.getElementsByTagName('DIV').item(0).appendChild(canvas);
		ctx = canvas.getContext('2d');
		/* background */
		ctx.fillStyle = 'black';
		ctx.fillRect(0, 0, canvas.width, canvas.height);
		/* getSumDec */
		ctx.fillStyle  = 'gray';
		ctx.beginPath();
		ctx.moveTo(canvas.width, canvas.height);
		ctx.lineTo(0, canvas.height);
		for (var i = 0; i < testData.cnt; i++) {
			ctx.lineTo(dx * i, (1 - this.getSumDec(testData.cnt, i)) * canvas.height);
		}
		ctx.closePath();
		ctx.fill();
		/* getSumInc */
		ctx.fillStyle  = 'silver';
		ctx.beginPath();
		ctx.moveTo(canvas.width, canvas.height);
		ctx.lineTo(0, canvas.height);
		for (var i = 0; i < testData.cnt; i++) {
			ctx.lineTo(dx * i, (1 - this.getSumInc(testData.cnt, i)) * canvas.height);
		}
		ctx.closePath();
		ctx.fill();
	}
};

function cLineReduce(in_srcLen, in_dstLen, in_dstOrgS, in_dstOrgE)
{
	/*
		|----|----|====|----|----|
		^										: dstOrgB[0]
		     ^									: dstOrgB[1]
		          ^								: dstOrg.S
		               ^						: dstOrg.E
		                    ^					: dstOrgA[0]
		                         ^				: dstOrgA[1]
		|--------|-----|====|-----|--------|
		^										: srcOrgB[0]
		         ^								: srcOrgB[1]
		               ^						: srcOrg.S
		                    ^					: srcOrg.E
		                          ^				: srcOrgA[0]
	                                       ^	: srcOrgA[1]
	*/

	/* src */
	this.srcLen = in_srcLen;
	var orgLen = in_dstOrgE - in_dstOrgS;
	var dst2src = (in_srcLen - orgLen) / (in_dstLen - orgLen);
	this.srcOrg = {
		S : Math.ceil(in_dstOrgS * dst2src),
		E : Math.ceil(in_dstOrgS * dst2src + orgLen)
	};
	this.srcOrgB = [];
	this.srcOrgA = [];
	/* dst */
	this.dstLen = in_dstLen;
	this.dstOrg = {
		S : in_dstOrgS,
		E : in_dstOrgE
	};
	this.dstOrgB = [];
	this.dstOrgA = [];
	/* OrgB (Before) */
	for (var step = 1; step < gRatio.getSteps(); step++) {
		var minSrc = this.srcOrg.S * gRatio.getLength(step, 0);
		var minDst = this.dstOrg.S * (1 / step);
		DP('step : ' + step + ', (' + minSrc + ', ' + minDst + ')');
		if (minSrc < minDst) {
			DP('... break');
			break;
		}
		this.srcOrgB[0] = 0;
		this.dstOrgB[0] = 0;
		for (var i = 1; i < step; i++) {
			this.srcOrgB[i] = Math.ceil(this.srcOrg.S * gRatio.getSumDec(step, (i - 1)));
			this.dstOrgB[i] = Math.ceil(this.dstOrg.S * (i / step));
		}
	}
	/* OrgA (After) */
	for (var step = 1; step < gRatio.getSteps(); step++) {
		var minSrc = (this.srcLen - this.srcOrg.E) * gRatio.getLength(step, 0);
		var minDst = (this.dstLen - this.dstOrg.E) * (1 / step);
		DP('step : ' + step + ', (' + minSrc + ', ' + minDst + ')');
		if (minSrc < minDst) {
			DP('... break');
			break;
		}
		for (var i = 0; i < step; i++) {
			this.srcOrgA[i] = Math.ceil(this.srcOrg.E + (this.srcLen - this.srcOrg.E) * gRatio.getSumInc(step, i));
			this.dstOrgA[i] = Math.ceil(this.dstOrg.E + (this.dstLen - this.dstOrg.E) * ((i + 1) / step));
		}
	}
}

cLineReduce.prototype = {
	srcPosArr : function() {
		return [].concat(this.srcOrgB, [this.srcOrg.S, this.srcOrg.E], this.srcOrgA);
	},
	dstPosArr : function() {
		return [].concat(this.dstOrgB, [this.dstOrg.S, this.dstOrg.E], this.dstOrgA);
	}
};

function test()
{
	var testData = {
		srcLen  : 600,
		dstLen  : 200,
		dstOrgS : 45,
		dstOrgE : 185
	};
	var line = new cLineReduce(
		testData.srcLen,
		testData.dstLen,
		testData.dstOrgS,
		testData.dstOrgE
	);
	DP(line);
	/* utility */
	var conf = {
		margin : 20,
		memory : 2
	};
	var drawLineUtil = (function(in_conf) {
		return function(in_ctx, in_color, in_lineCnt, in_start, in_len) {
			in_ctx.strokeStyle = in_color;
			in_ctx.beginPath();
			if (in_len > 0) {
				in_ctx.moveTo(in_conf.margin + in_start, in_conf.margin * in_lineCnt);
				in_ctx.lineTo(in_conf.margin + in_start + in_len, in_conf.margin * in_lineCnt);
			} else {
				in_ctx.moveTo(in_conf.margin + in_start, in_conf.margin * in_lineCnt - in_conf.memory);
				in_ctx.lineTo(in_conf.margin + in_start, in_conf.margin * in_lineCnt + in_conf.memory);
			}
			in_ctx.stroke();
		};
	})(conf);
	/* canvas */
	var canvas = document.createElement('CANVAS');
	canvas.width = testData.srcLen + conf.margin * 2;
	canvas.height = conf.margin * 3;
	document.write('<div></div>');
	document.getElementsByTagName('DIV').item(0).appendChild(canvas);
	ctx = canvas.getContext('2d');
	/* src */
	drawLineUtil(ctx, 'black', 1, 0, testData.srcLen);
	var arr = line.srcPosArr();
	for (var i = 0; i < arr.length; i++) {
		drawLineUtil(ctx, 'green', 1, arr[i], 0);
	}
	/* dst */
	drawLineUtil(ctx, 'black', 2, 0, testData.dstLen);
	var arr = line.dstPosArr();
	for (var i = 0; i < arr.length; i++) {
		drawLineUtil(ctx, 'green', 2, arr[i], 0);
	}
}

var dGyoGan = {
	windowMargin : 0.1,
	reduceMargin : {
		w : -1,
		h : -1
	},
	orgSize : {
		w : 0.4,
		h : 0.4
	},
	bind : function(prop) {
		var args = null;
		if (arguments.length > 1) {
			args = Array.prototype.slice.call(arguments);
			args.shift();
		}
		return (function(self, in_args) {
			if (in_args) {
				return function() {
					self[prop].apply(self, in_args);
				};
			} else {
				return function() {
					self[prop].call(self);
				};
			}
		})(this, args);
	},
	img : document.createElement('IMG'),
	canvas : document.createElement('CANVAS'),
	start : function(in_fname, in_org_w, in_org_h) {
		if (in_org_w > 0) {
			this.orgSize.w = in_org_w;
		}
		if (in_org_h > 0) {
			this.orgSize.h = in_org_h;
		}
		this.img.src = in_fname;
		this.img.addEventListener('load', this.bind('resetCanvas'), false);
		window.addEventListener('resize', this.bind('resetCanvas'), false);
		return this.canvas;
	},
	resetCanvas : function() {
		this.canvas.width = Math.min(window.innerWidth * (1 - this.windowMargin), this.img.width * 0.7);
		this.canvas.height = Math.min(window.innerHeight * (1 - this.windowMargin), this.img.height * 0.7);
		this.reduceMargin.w = (1 - this.orgSize.w) / 2 * this.canvas.width;
		this.reduceMargin.h = (1 - this.orgSize.h) / 2 * this.canvas.height;
		this.reDraw(0, 0);
	},
	reDraw : function(in_dx, in_dy) {
		if (Math.abs(in_dx) > this.reduceMargin.w) {
			return;
		}
		if (Math.abs(in_dy) > this.reduceMargin.h) {
			return;
		}
		var dx = in_dx + this.reduceMargin.w;
		var dy = in_dy + this.reduceMargin.h;
		/* width */
		var xLine = new cLineReduce(
			this.img.width,
			this.canvas.width,
			dx,
			dx + this.orgSize.w * this.canvas.width
		);
		/* height */
		var yLine = new cLineReduce(
			this.img.height,
			this.canvas.height,
			dy,
			dy + this.orgSize.h * this.canvas.height
		);
		ySrcPos = yLine.srcPosArr();
		xSrcPos = xLine.srcPosArr();
		yDstPos = yLine.dstPosArr();
		xDstPos = xLine.dstPosArr();
		var ctx = this.canvas.getContext('2d');
		for (var i = 0; i < ySrcPos.length - 1; i++) {
			for (var j = 0; j < xSrcPos.length - 1; j++) {
				/* src --> dst */
				ctx.drawImage(this.img,
					xSrcPos[j], ySrcPos[i], (xSrcPos[j + 1] - xSrcPos[j]), (ySrcPos[i + 1] - ySrcPos[i]),
					xDstPos[j], yDstPos[i], (xDstPos[j + 1] - xDstPos[j]), (yDstPos[i + 1] - yDstPos[i]));
			}
		}
	}
};

var gDrag = {
	evBind : function(prop) {
		return (function(self) {
			return function() {
				if (arguments.length > 0) {
					self[prop].apply(self, Array.prototype.slice.call(arguments));
				} else {
					self[prop].call(self);
				}
			};
		})(this);
	},
	hook : {
		func : null,
		fire : -1,
		wait : 20
	},
	autoPosition : {
		enable : false,
		interval : -1,
		timer : null,
		threshold : 10
	},
	start : function(in_elem, in_hook, in_autoPosition) {
		this.hook.func = in_hook;
		if (in_autoPosition) {
			this.autoPosition.enable = in_autoPosition;
			this.autoPosition.interval = this.hook.wait * 2;
		}
		var events = ['mousemove', 'mouseout', 'mousedown', 'mouseup'];
		for (var i = 0; i < events.length; i++) {
			in_elem.addEventListener(events[i], this.evBind(events[i]), false);
		}
	},
	pos : {
		curr : {
			x : 0,
			y : 0
		},
		drag : null
	},
	mousemove : function(in_event) {
		if (this.pos.drag) {
			var dx = this.pos.curr.x + in_event.clientX - this.pos.drag.x;
			var dy = this.pos.curr.y + in_event.clientY - this.pos.drag.y;
			var now = (+new Date());
			if (now - this.hook.fire > this.hook.wait) {
				this.hook.fire = now;
				(this.hook.func)(dx, dy);
			}
		}
	},
	mouseout : function(in_event) {
		this.mouseup(in_event);
	},
	mousedown : function(in_event) {
		if (!this.pos.drag) {
			this.pos.drag = {
				x : in_event.clientX,
				y : in_event.clientY
			};
		}
	},
	mouseup : function(in_event) {
		if (this.pos.drag) {
			this.pos.curr.x = in_event.clientX - this.pos.drag.x;
			this.pos.curr.y = in_event.clientY - this.pos.drag.y;
			this.pos.drag = null;
			if (this.autoPosition.enable) {
				this.autoPosition.timer = window.setInterval(
					this.evBind('timerProgress'), this.autoPosition.interval);
			}
		}
	},
	timerProgress : function() {
		var ap = this.autoPosition;
		dx = this.pos.curr.x / 2;
		dy = this.pos.curr.y / 2;
		if ((Math.abs(dx) < ap.threshold) && (Math.abs(dy) < ap.threshold)) {
			window.clearTimeout(ap.timer);
			dx = 0;
			dy = 0;
		}
		(this.hook.func)(dx, dy);
		this.pos.curr.x = dx;
		this.pos.curr.y = dy;
	}
};

function getQueryParam(in_default)
{
	var ret = in_default;
	if (!location.search) {
		return ret;
	}
	var params = location.search.split('?')[1].split('&');
	for (var i = 0; i < params.length; i++) {
		var fv = params[i].split('=');
		if (fv.length < 2) {
			continue;
		}
		if (fv[0] in ret) {
			if (typeof(ret[fv[0]]) == 'number') {
				ret[fv[0]] = +fv[1];
			} else {
				ret[fv[0]] = fv[1];
			}
		}
	}
	return ret;
}

/*
gRatio.test();
test();
*/

gRatio.configure(0.4, 8);
var param = getQueryParam({
	i : '45snobo.jpg',
	w : 0.3,
	h : 0.3,
	pull : 1
});
console.log(param);
var elem = dGyoGan.start(param.i, param.w, param.h);

document.write('<ins></ins>');
document.getElementsByTagName('INS').item(0).appendChild(elem);

gDrag.start(elem, function(in_x, in_y) {
	if (param.pull) {
		dGyoGan.reDraw(-in_x, -in_y);
	} else {
		dGyoGan.reDraw(in_x, in_y);
	}
}, true);

</script>
<hr />
<a style='text-decoration:none;' href='gyoGan-dynamic.html?pull=1'>[image-pull]</a>
<a style='text-decoration:none;' href='gyoGan-dynamic.html?pull=0'>[point-focus]</a>
